package com.example.oauth.config;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import com.sinoframework.web.servlet.bean.Response;
import com.sinoframework.web.servlet.bean.ResponseResult;
import com.sinoframework.web.servlet.security.HttpServletResponseHttpOutputMessage;

@Configuration
public class BeanConfiguration {
	
	
	private static final Logger log = LogManager.getLogger();
	
	/**
	 *	 在spring5.0之后，springsecurity client密码 和 user密码必须用 PasswordEncoder 加密
	 * @return
	 */
    @Bean
    public BCryptPasswordEncoder bcryptPasswordEncoder(){
    	return new BCryptPasswordEncoder();
    }
    
    
    
    /**
     * 	自定义 用户表和权限表 就需要自己实现一下三个接口：
     * 		UserDetailsService 参考:org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
     * 		UserDetails 参考：org.springframework.security.core.userdetails.User
     *      GrantedAuthority 参考:org.springframework.security.core.authority.SimpleGrantedAuthority
     *      
     *	 以下使用springsecurity定义的用户信息表
     * 
     * @param dataSource
     * @return
     */
    @Bean
    UserDetailsService userDetails(@Autowired DataSource dataSource){
    	JdbcDaoImpl dao = new JdbcDaoImpl();
    	dao.setDataSource(dataSource);
        return dao;
    }
	
    

    /**
     * 	自定义客户端表实现以下接口：
     * 		ClientDetailsService 参考：org.springframework.security.oauth2.provider.client.JdbcClientDetailsService
     * 
     * 
     * 	以下使用springsecurity定义的客户端表
     * 
     * @param dataSource
     * @return
     */
    @Bean
    @Primary
    ClientDetailsService customClientDetailsService(@Autowired DataSource dataSource) { 
    	return new JdbcClientDetailsService(dataSource);
    }
    


	
    
    
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Bean
	public AuthenticationSuccessHandler authenticationSuccessHandler(@Autowired List<HttpMessageConverter<?>> messageConverters) {
    	return ( request,  response,authentication)->{
    		
    		ResponseResult<String>  res = Response.makeAuthorSuccess(null);
			HttpHeaders httpHeaders = new HttpHeaders();
			httpHeaders.setContentType(MediaType.APPLICATION_JSON);
			
			for (HttpMessageConverter messageConverter : messageConverters) {
				if (messageConverter.canWrite(res.getClass(), MediaType.APPLICATION_JSON)) {
					messageConverter.write(res, 
							MediaType.APPLICATION_JSON, 
							new HttpServletResponseHttpOutputMessage(httpHeaders,response));
					
					return;
				}
			}
    	};
	}
    
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
    @Bean
	public AuthenticationFailureHandler customAuthenticationFailureHandler(@Autowired List<HttpMessageConverter<?>> messageConverters) {
		return (HttpServletRequest request,
				HttpServletResponse response, AuthenticationException exception)->{
					ResponseResult<String>  res = Response.makeAuthorFail(exception.getMessage());
					HttpHeaders httpHeaders = new HttpHeaders();
					httpHeaders.setContentType(MediaType.APPLICATION_JSON);
					
					for (HttpMessageConverter messageConverter : messageConverters) {
						if (messageConverter.canWrite(res.getClass(), MediaType.APPLICATION_JSON)) {
							messageConverter.write(res, 
									MediaType.APPLICATION_JSON, 
									new HttpServletResponseHttpOutputMessage(httpHeaders,response));
							
							return;
						}
					}
		};
	}
    
	

    /**
     * 
     * 	自定义【认证】异常处理 AuthenticationException 
     * @param messageConverters
     * @return
     */
    @Bean
    public AuthenticationEntryPoint customAuthenticationEntryPoint(@Autowired List<HttpMessageConverter<?>> messageConverters) {
    	
    	return new AuthenticationEntryPoint() {
    		
			@SuppressWarnings({ "unchecked", "rawtypes" })
			@Override
			public void commence(HttpServletRequest request, HttpServletResponse response,
					AuthenticationException authException) throws IOException, ServletException {
				
				ResponseResult<String>  res = Response.makeAuthorFail(authException.getMessage());
				HttpHeaders httpHeaders = new HttpHeaders();
				httpHeaders.setContentType(MediaType.APPLICATION_JSON);
				
				for (HttpMessageConverter messageConverter : messageConverters) {
					if (messageConverter.canWrite(res.getClass(), MediaType.APPLICATION_JSON)) {
						messageConverter.write(res, 
								MediaType.APPLICATION_JSON, 
								new HttpServletResponseHttpOutputMessage(httpHeaders,response));
						
						return;
					}
				}
				
				
			}
    		
    	};
    }
    
    
    /**
     * 
     * 	自定义【鉴权】异常处理 AccessDeniedException
     * @param messageConverters
     * @return
     */
    @Bean
    public AccessDeniedHandler customAccessDeniedHandler(@Autowired List<HttpMessageConverter<?>> messageConverters) {
    	
    	return new AccessDeniedHandler() {
    		
    		@SuppressWarnings({ "unchecked", "rawtypes" })
			@Override
			public void handle(HttpServletRequest request, HttpServletResponse response,
					AccessDeniedException accessDeniedException) throws IOException, ServletException {
				
				ResponseResult<String>  res = Response.makeAccessDenied(accessDeniedException.getMessage());
				HttpHeaders httpHeaders = new HttpHeaders();
				httpHeaders.setContentType(MediaType.APPLICATION_JSON);
				
				for (HttpMessageConverter messageConverter : messageConverters) {
					if (messageConverter.canWrite(res.getClass(), MediaType.APPLICATION_JSON)) {
						messageConverter.write(res, 
								MediaType.APPLICATION_JSON, 
								new HttpServletResponseHttpOutputMessage(httpHeaders,response));
						
						return;
					}
				}
				
			}
    		
    	};
    }
    
    
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Bean
    public LogoutSuccessHandler logoutSuccessHandler(@Autowired List<HttpMessageConverter<?>> messageConverters) {
    	
    	return (request, response,authentication)->{
    		
    		
    		ResponseResult<String>  res = Response.makeOKRsp(ReflectionToStringBuilder.toString(authentication)+"退出成功");
			HttpHeaders httpHeaders = new HttpHeaders();
			httpHeaders.setContentType(MediaType.APPLICATION_JSON);
			
			for (HttpMessageConverter messageConverter : messageConverters) {
				if (messageConverter.canWrite(res.getClass(), MediaType.APPLICATION_JSON)) {
					messageConverter.write(res, 
							MediaType.APPLICATION_JSON, 
							new HttpServletResponseHttpOutputMessage(httpHeaders,response));
					
					return;
				}
			}
    		
    	};
    }
    
    
}
